// Cell names have been changed in this file by netl_namemap on Mon Jan  3 04:00:15 UTC 2022
////////////////////////////////////////////////////////////////////////////// 
//
//  pcs_raw_rx_ctl_cdr.v
//
//  RX control
//
//  Original Author: Yidi Lin 
//  Current Owner: Yidi Lin   
//
////////////////////////////////////////////////////////////////////////////// 
//
// Copyright (C) 2016 Synopsys, Inc.  All rights reserved.
//
// SYNOPSYS CONFIDENTIAL - This is an unpublished, proprietary work of
// Synopsys, Inc., and is fully protected under copyright and trade secret
// laws.  You may not view, use, disclose, copy, or distribute this file or
// any information contained herein except pursuant to a valid written
// license agreement. It may not be used, reproduced, or disclosed to others
// except in accordance with the terms and conditions of that agreement.
//
////////////////////////////////////////////////////////////////////////////// 
//
//    Perforce Information
//    $Author: gdube $
//    $File: //dwh/up16/main/dev/pcs_raw/dig/rtl/pcs_raw_rx_ctl_cdr.v $
//    $DateTime: 2019/08/30 04:50:18 $
//    $Revision: #5 $
//
////////////////////////////////////////////////////////////////////////////// 

`timescale 1ns/10fs

module dwc_e12mp_phy_x4_ns_pcs_raw_rx_ctl_cdr (
  // global signals
  input wire           cr_clk,
  input wire           cr_rx_rst,

  input wire           rx_clk,
  input wire           rx_clk_rst,

  // signals for lane configuration
  input wire [2:0]     ref_range,  
  input wire [5:0]     rx_pma_ref_ld_val_i,
  input wire [12:0]    rx_pma_vco_ld_val_i,
  input wire [1:0]     rx_pma_rate_i,
  input wire [1:0]     rx_pma_width_i,
  input wire [1:0]     rx_pma_pstate_i,
  input wire           rx_data_en_i,
  input wire           rx_pma_req_i,
  input wire           rx_pma_valid_i,
  input wire [4:0]     rx_cdr_ppm_max_i,

  // signals for cdr control 
  input wire [11:0]    rx_cdr_recovery_time_r,
  input wire           rx_cdr_detect_en_r,
  input wire           rx_cdr_ppm_monitor_mode_r,
  input wire           rx_cdr_detector_dis_in_adapt_r,
  input wire           rx_adapt_req_sync,

  output wire          rx_cdr_data_en,
  output reg [13:0]    rx_ppm_drift,
  output reg           rx_ppm_drift_vld,
  output reg [2:0]     rx_cdr_det_state,
  output reg           rx_ppm_drift_oflow
);

// PARAMETERS --------------------------------------
// Satte parameters
localparam stIDLE              = 3'd0;
localparam stCNT_WAIT          = 3'd1;
localparam stRX_CNT_CAPTURE    = 3'd2;
localparam stDATA_EN_LOW       = 3'd3;
localparam stCDR_RECOVERY_WAIT = 3'd4; 

wire [1:0]  rx_pma_rate_int;
wire [1:0]  rx_pma_pstate_int;
wire [1:0]  rx_pma_width_int;
wire [4:0]  rx_cdr_ppm_max_int;
wire [12:0] vco_ld_val_int;
reg  [1:0]  rx_pma_rate;
reg  [1:0]  rx_pma_pstate;
reg  [1:0]  rx_pma_width;
reg  [4:0]  rx_cdr_ppm_max;
reg  [12:0] vco_ld_val;

// Synchronizer for rx_data_en_i
wire rx_data_en_cr_clk;
dwc_e12mp_phy_x4_ns_gen_sync sync_rx_data_en (
    .q   (rx_data_en_cr_clk),
    .clk (cr_clk),
    .rst (cr_rx_rst),
    .d   (rx_data_en_i)
);

// Synchronizer for rx_data_en_i
wire rx_pma_valid_cr_clk;
dwc_e12mp_phy_x4_ns_gen_sync sync_rx_pma_valid (
    .q   (rx_pma_valid_cr_clk),
    .clk (cr_clk),
    .rst (cr_rx_rst),
    .d   (rx_pma_valid_i)
);

// VCO frequency = (baud_rate /2 ) * 2^rx_rate
// RX_clk frequency = (baud_rate / rx_width

// ref_ld_val * 4 (rx_width == 8 or 10) or 8 (rx_width == 16 or 20)
// the TX is running at full rate (rising and falling edge)
wire [8:0] ref_ld_val_4or5 = {rx_pma_ref_ld_val_i, 2'b00} << rx_pma_width[1];
// Need to add ref_ld_val * 1 (rx_width = 10) or 2 (rx_width == 20) 
wire [6:0]  ref_ld_val_1or2  = rx_pma_ref_ld_val_i << rx_pma_width[1];
// final ref_ld_val adjusted with rx_width 
wire [9:0] ref_ld_val_width = {1'b0, ref_ld_val_4or5} + (rx_pma_width[0] ? {3'b000, ref_ld_val_1or2} : 11'd0); 
// final ref_ld_val adjusted with rx_rate
// if cr_clk is div2 version of the ref_dig_clk, further divide the ref ld val by 2
`ifdef DWC_FPGA_MODE
  // clock adjustment for TC gasket
  wire [15:0] ref_ld_val_adj = ((ref_ld_val_width << rx_pma_rate) >> ref_range[2]) << 2;
`else
  wire [13:0] ref_ld_val_adj = (ref_ld_val_width << rx_pma_rate) >> ref_range[2];
`endif


// Start the CDR state machine if data_en == 1 and rx_valid == 1and it is enable (from creg)
wire lane_rdy_for_cdr_det = (rx_data_en_cr_clk ) & rx_pma_valid_cr_clk;
wire cdr_lock_detector_en = lane_rdy_for_cdr_det & rx_cdr_detect_en_r ;
wire rx_cdr_ppm_mon_en    = lane_rdy_for_cdr_det & rx_cdr_ppm_monitor_mode_r; 

// Update rx_cdr_det_state
reg  [2:0]   next_state;
wire         cdr_reset;
always @(posedge cr_clk or posedge cr_rx_rst) 
begin
  if(cr_rx_rst) begin
    rx_cdr_det_state <= stIDLE;
  end else begin
    if (cdr_reset) begin
      rx_cdr_det_state <= stIDLE;
    end else begin
      rx_cdr_det_state <= next_state;
    end
  end
end


// State Machine Signals
reg         counter_en;
reg         counter_en_int;
wire        counter_en_cr_clk;
reg         force_cdr_data_en_low;
reg         rx_cnt_drift_vld;
//Mantis9148 (STAR 9001497055) -  Update RX CDR PPM FSM to prevent glitch on rxX_data_en
reg         rx_cdr_ppm_data_en;
wire [13:0] rx_cnt_drift_neg;
wire [12:0] rx_cnt_drift_abs;

`ifdef DWC_E12MP_X4NS_FPGA_MODE
  reg  [15:0] ref_cnt;
`else
  reg  [13:0] ref_cnt;
`endif
wire        ref_cnt_eq_zero;

wire        recovery_cnt_eq_zero;
reg         recovery_cnt_en;
reg  [11:0] recovery_cnt;

reg         rx_ppm_drift_oflow_int;

wire rate_changed;
// CDC is reset if 
// 1. rate change
// 2. going to pstate other than p0 and p0s
assign cdr_reset = rate_changed | (rx_pma_pstate != 2'b00 && rx_pma_pstate != 2'b01) | (rx_adapt_req_sync & rx_cdr_detector_dis_in_adapt_r);

// State Machine Boday for CDR Detector
always @*
begin
  next_state              = stIDLE;
  counter_en_int          = 1'b0;
  recovery_cnt_en         = 1'b0;
  force_cdr_data_en_low   = 1'b0;  
  rx_cnt_drift_vld        = 1'b0;
  rx_ppm_drift_oflow_int  = 1'b0;

  case (rx_cdr_det_state)
   stIDLE:
   // If CDR Detector enabled,  enable both the ref_cnt and rx_cnt
   begin
       if(cdr_lock_detector_en | rx_cdr_ppm_mon_en) begin
           // If in this state while cdr_reset is low, keep the 
           // counter disabled
           counter_en_int = ~cdr_reset;
           next_state     = stCNT_WAIT;
       end else begin
           next_state     = stIDLE;
       end
   end
   stCNT_WAIT:
   // Wait until ref_cnt reaches 0
   // ref_cnt is a count-down counter with initial value of ref_ld_val_adj
   begin
       if(cdr_lock_detector_en | rx_cdr_ppm_mon_en) begin
           if(ref_cnt_eq_zero) begin
               next_state = stRX_CNT_CAPTURE;
           end else begin
               counter_en_int = 1'b1;
               next_state     = stCNT_WAIT;
           end
       end else begin
           next_state = stIDLE;
       end
   end
   stRX_CNT_CAPTURE:
   // Wait for counter_en_cr_clk (counter_en from rx_clk) to go low. This makes sure the rx_cdr_drift from rx_clk is stable to avoid CDC problem
   // If the rx_cnt_drift_abs is less than the allowable max drift, starts it over (goes to stIDLE)
   // If the rx_cnt_drift_abs is larger than the allowable max drift, perform CDR recovery by forcing the data_en low and wait
   begin
       if(cdr_lock_detector_en) begin
           if(~counter_en_cr_clk) begin
               rx_ppm_drift_oflow_int = |rx_cnt_drift_abs[12:5];
               if(rx_ppm_drift_oflow_int || (rx_cnt_drift_abs[4:0] >= rx_cdr_ppm_max)) begin
                   next_state = stDATA_EN_LOW;
               end else begin
                   next_state = stIDLE;
               end
               rx_cnt_drift_vld = 1;
           end else begin
               next_state = stRX_CNT_CAPTURE;
           end
       end else if(rx_cdr_ppm_mon_en) begin
           if(~counter_en_cr_clk) begin
               rx_ppm_drift_oflow_int = |rx_cnt_drift_abs[12:5];
               rx_cnt_drift_vld   = 1;
               next_state         = stIDLE;
           end else begin
               next_state = stRX_CNT_CAPTURE;
           end
       end else begin
           next_state = stIDLE;
       end
   end
   stDATA_EN_LOW:
   // Force rx_pma_data_i 
   // if rx_pma_valid_i goes low, start the CDR recovery counter (recovery_cnt)
   begin
       if(~rx_pma_valid_cr_clk) begin
           next_state = stCDR_RECOVERY_WAIT;
           recovery_cnt_en = 1'b1;
       end else begin 
           next_state = stDATA_EN_LOW;
       end
       force_cdr_data_en_low = 1'b1;
   end 
   stCDR_RECOVERY_WAIT:
   // Wait until the CDR recovery_cnt reaches 0
   begin
       if(recovery_cnt_eq_zero) begin
           next_state = stIDLE;
       end else begin
           recovery_cnt_en = 1'b1;
           next_state = stCDR_RECOVERY_WAIT;
       end
       force_cdr_data_en_low = 1'b1;
   end
  default:
  begin
      next_state = stIDLE;
  end
  endcase
end

// recovery_cnt for waiting for CDR recovery, enable by state machine
assign recovery_cnt_eq_zero = ~(|recovery_cnt);
always @(posedge cr_clk or posedge cr_rx_rst) 
begin
    if(cr_rx_rst) begin
        recovery_cnt <= 12'd0;
    end else begin
        if(~recovery_cnt_en) begin
            recovery_cnt <= rx_cdr_recovery_time_r;
        end else if(!recovery_cnt_eq_zero) begin
            recovery_cnt <= recovery_cnt - 12'd1;
        end
    end
end

// flop the counter_en for synchronization and for ref_cnt
always @(posedge cr_clk or posedge cr_rx_rst) begin
    if(cr_rx_rst) begin
        counter_en <= 1'd0;
    end else begin
        counter_en <= counter_en_int;
    end
end

// ref_cnt enablbed by counter_en from state machine
assign ref_cnt_eq_zero = ~(|ref_cnt);
always @(posedge cr_clk or posedge cr_rx_rst) 
begin
    if(cr_rx_rst) begin
`ifdef DWC_E12MP_X4NS_FPGA_MODE
        ref_cnt <= 0;
`else
        ref_cnt <= 14'd0;
`endif    
    end else begin
        if(~counter_en_int) begin
            ref_cnt <= ref_ld_val_adj;
        end else if(counter_en_int && !ref_cnt_eq_zero) begin
`ifdef DWC_E12MP_X4NS_FPGA_MODE
            ref_cnt <= ref_cnt - 1;
`else
            ref_cnt <= ref_cnt - 14'd1;
`endif
        end
    end
end

// capture rx_cnt_dirft value
always @(posedge cr_clk or posedge cr_rx_rst) 
begin
    if(cr_rx_rst) begin
        rx_ppm_drift      <= 14'd0;
        rx_ppm_drift_vld  <= 1'b0;
        rx_ppm_drift_oflow <= 1'b0;
    end else if(cdr_reset | (~counter_en_int & counter_en))begin
        rx_ppm_drift      <= 14'd0;
        rx_ppm_drift_vld  <= 1'b0;
        rx_ppm_drift_oflow <= 1'b0;
    end else if(rx_cnt_drift_vld)begin
        rx_ppm_drift      <= rx_cnt_drift_neg;
        rx_ppm_drift_vld  <= 1'b1;
        rx_ppm_drift_oflow <= rx_ppm_drift_oflow_int;
    end
end

// Sychronizer for counter_en signals from rx_clk domain back to cr_clk domain
// This is served as capture signals for rx_cnt_drift
wire counter_en_rx_clk;
reg  counter_en_rx_clk_d1;
dwc_e12mp_phy_x4_ns_gen_sync sync_count_en_rxclk2crclk (
    .q   (counter_en_cr_clk),
    .clk (cr_clk),
    .rst (cr_rx_rst),
    .d   (counter_en_rx_clk_d1)
);

// Sychronizer for rx_pma_req_i signals (Asynchronous)
wire rx_pma_req_cr_clk;
dwc_e12mp_phy_x4_ns_gen_sync sync_rx_pma_req (
    .q   (rx_pma_req_cr_clk),
    .clk (cr_clk),
    .rst (cr_rx_rst),
    .d   (rx_pma_req_i)
);

// Delay the rx_pma_req generate a signals for capturing the vco_ld_val
// rx_in_reset signals indicated if the rx_clk domain is in reset
reg rx_in_reset;
reg rx_pma_req_cr_clk_d1;
always @(posedge cr_clk or posedge cr_rx_rst)
begin
    if(cr_rx_rst) begin
        rx_pma_req_cr_clk_d1 <= 1'b0;
        rx_in_reset          <= 1'b1;
    end else begin
        rx_pma_req_cr_clk_d1 <= rx_pma_req_cr_clk;

        if(rx_in_reset) begin
            rx_in_reset <= 1'b0;
        end
    end
end

// Capture the updated inputs if one of the following. By spec, the inputs must have been stable with request.
// 1. new req
// 2. just out of reset 
wire rx_pma_req_cr_clk_rising = ~rx_pma_req_cr_clk_d1 & rx_pma_req_cr_clk;
wire new_req                  = rx_pma_req_cr_clk_rising || rx_in_reset; 

dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(13)) vco_ld_val_gen_mux (
  .out (vco_ld_val_int),
  .sel (new_req),
  .d0  (vco_ld_val),
  .d1  (rx_pma_vco_ld_val_i)
);

dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(2)) rx_pstate_gen_mux (
  .out (rx_pma_pstate_int),
  .sel (new_req),
  .d0  (rx_pma_pstate),
  .d1  (rx_pma_pstate_i)
);

dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(2)) rx_rate_gen_mux (
  .out (rx_pma_rate_int),
  .sel (new_req),
  .d0  (rx_pma_rate),
  .d1  (rx_pma_rate_i)
);

dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(2)) rx_width_gen_mux (
  .out (rx_pma_width_int),
  .sel (new_req),
  .d0  (rx_pma_width),
  .d1  (rx_pma_width_i)
);

dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(5)) rx_cdr_ppm_max_gen_mux (
  .out (rx_cdr_ppm_max_int),
  .sel (new_req),
  .d0  (rx_cdr_ppm_max),
  .d1  (rx_cdr_ppm_max_i)
);

always @(posedge cr_clk or posedge cr_rx_rst) 
begin
    if(cr_rx_rst) begin
        vco_ld_val         <= 13'd0;
        rx_pma_rate        <= 2'd0;
        rx_pma_pstate      <= 2'd0; 
        rx_pma_width       <= 2'd0; 
        rx_cdr_ppm_max     <= 5'd0;
        rx_cdr_ppm_data_en <= 1'b0;
    end else begin
        vco_ld_val         <= vco_ld_val_int;
        rx_pma_rate        <= rx_pma_rate_int;
        rx_pma_pstate      <= rx_pma_pstate_int; 
        rx_pma_width       <= rx_pma_width_int; 
        rx_cdr_ppm_max     <= rx_cdr_ppm_max_int;
        //Mantis9148 (STAR 9001497055) -  Update RX CDR PPM FSM to prevent glitch on rxX_data_en
        rx_cdr_ppm_data_en <= ~force_cdr_data_en_low & rx_data_en_cr_clk;
    end
end

assign rate_changed = rx_pma_rate != rx_pma_rate_int;

// -------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------
// RX clock domain Logic
// -------------------------------------------------------------------------------------------
// Sychronizer for counter_en signals from cr_clk domain
dwc_e12mp_phy_x4_ns_gen_sync sync_count_en_crclk2rxclk (
    .q   (counter_en_rx_clk),
    .clk (rx_clk),
    .rst (rx_clk_rst),
    .d   (counter_en)
);


// Delay the counter_en signals to generate rising edge of it and use it to reload the rx_cnt
always @(posedge rx_clk or posedge rx_clk_rst) 
begin
    if(rx_clk_rst) begin
        counter_en_rx_clk_d1 <= 1'b0;
    end else begin
        counter_en_rx_clk_d1 <= counter_en_rx_clk;
    end
end
wire rx_cnt_reload = (~counter_en_rx_clk_d1) & counter_en_rx_clk;

// rx_cnt logic
reg signed [13:0] rx_cnt;
wire rx_cnt_saturated = (rx_cnt == 14'b1_0000_0000_0000_1);
always @(posedge rx_clk or posedge rx_clk_rst) 
begin
    if(rx_clk_rst) begin
        rx_cnt <= 14'd0;
    end else begin
        if(rx_cnt_reload) begin
            rx_cnt <= {1'b0, vco_ld_val};
        end else if(counter_en_rx_clk_d1 & (~rx_cnt_saturated)) begin
            rx_cnt <= rx_cnt - 14'd1;
        end 
    end
end

// reverse the sign of rx_cnt (2's complement number)
// this rx_cnt_drift[13] == 1'b0, if rx_clk runs faster than expected
// this rx_cnt_drift[13] == 1'b1, if rx_clk rans slower than expected
assign rx_cnt_drift_neg      = ~rx_cnt+14'd1;
// take the absoute value 
assign rx_cnt_drift_abs = rx_cnt[13] ? rx_cnt_drift_neg[12:0] : rx_cnt[12:0];

// -------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------
// Final Data enable to PMA
//assign rx_cdr_data_en = rx_cdr_detect_en_r ? (~(force_cdr_data_en_low) & rx_data_en_cr_clk) : rx_data_en_cr_clk;
dwc_e12mp_phy_x4_ns_gen_mux data_en_gen_mux (
  .out (rx_cdr_data_en),
  .sel (rx_cdr_detect_en_r),
  .d0  (rx_data_en_i),
  .d1  (rx_cdr_ppm_data_en)
);

endmodule

